package cn.blu10ph.trustshare.service.impl;

import cn.blu10ph.trustshare.bean.msg.BaseMsg;
import cn.blu10ph.trustshare.bean.msg.TrustMsg;
import cn.blu10ph.trustshare.bean.node.GetPublicKeyMsg;
import cn.blu10ph.trustshare.bean.node.PublicKeyMsg;
import cn.blu10ph.trustshare.bean.node.TrustNode;
import cn.blu10ph.trustshare.constant.Constant;
import cn.blu10ph.trustshare.core.PGPManager;
import cn.blu10ph.trustshare.service.DHTMsgService;
import cn.blu10ph.trustshare.util.ConvertUtil;
import cn.blu10ph.trustshare.util.HashAndSignUtil;
import cn.blu10ph.trustshare.util.PgpUtil;
import com.fasterxml.jackson.core.JsonProcessingException;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.socket.DatagramPacket;
import lombok.extern.slf4j.Slf4j;
import org.bouncycastle.openpgp.PGPPublicKey;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

/**
 * <p> DHTGetPublicKeyMsgServiceImpl </p >
 *
 * @author cxx
 * @date 2024/4/6 22:22
 */
@Slf4j
@Component
public class DHTGetPublicKeyMsgServiceImpl extends DHTMsgBaseService<GetPublicKeyMsg> implements DHTMsgService<GetPublicKeyMsg> {

    @Override
    public void sendQuery(TrustNode node, GetPublicKeyMsg params) throws Exception {
        params.setFromNode(routingTable.getSelfNode());
        params.setFromNodePublicKey(PgpUtil.getPublicKeyEncode(PgpUtil.getPublicKey(PGPManager.getSecretKey())));
        super.sendQuery(node, params);
    }

    @Override
    public void sendReturn(ChannelHandlerContext ctx, DatagramPacket packet, TrustNode node, TrustMsg msg) {
        GetPublicKeyMsg getPublicKeyMsg;
        try {
            if(StringUtils.hasText(msg.getParams())) {
                getPublicKeyMsg = ConvertUtil.str2Obj(msg.getParams(), GetPublicKeyMsg.class);
            } else {
                getPublicKeyMsg = new GetPublicKeyMsg();
            }
        } catch (JsonProcessingException ex) {
            ex.printStackTrace();
            return;
        }

        try {
            node = getPublicKeyMsg.getFromNode();

            PGPPublicKey publicKeyPbj = PgpUtil.loadPublicKey(getPublicKeyMsg.getFromNodePublicKey());
            String fingerprint = PgpUtil.getPublicKeyFingerprint(publicKeyPbj);
            if(!fingerprint.equals(node.getNodeId())) {
                String errMsg = msg.getType() + " query node[" + node.getNodeId() + "] " +
                        "with publicKey fingerprint[" + fingerprint + "] " +
                        "is not equals to publicKey";
                log.error(errMsg);
                return;
            }
            if(!HashAndSignUtil.checkNodeSign(node, publicKeyPbj)){
                String errMsg = msg.getType() + " query node[" + node.getNodeId() + "] " +
                        "with publicKey fingerprint[" + node.getNodeId() + "] " +
                        "check sign is failed";
                log.error(errMsg);
                return;
            }
            pgpManager.savePublicKey(node.getNodeId(), publicKeyPbj);
            routingTable.addNode(node);
            routingTable.setNodeActive(node.getNodeId());
        } catch (Exception ex) {
            ex.printStackTrace();
            return;
        }

        PGPPublicKey publicKey = pgpManager.getPublicKey(getPublicKeyMsg.getTarget());

        BaseMsg msgObj = getBaseMsg(Constant.MSG_DIRECTION_RETURN, node.getNodeId());
        PublicKeyMsg publicKeyMsg = new PublicKeyMsg(getPublicKeyMsg);
        try {
            publicKeyMsg.setPublicKey(PgpUtil.getPublicKeyEncode(publicKey));
            publicKeyMsg.setNode(routingTable.getSelfNode());
            msgObj.setParams(ConvertUtil.obj2Str(publicKeyMsg));
            response(node, ctx, packet.sender(), msgObj);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    @Override
    public void receive(ChannelHandlerContext ctx, DatagramPacket packet, TrustNode node, TrustMsg msg) {
        try {
            PublicKeyMsg publicKeyMsg = ConvertUtil.str2Obj(msg.getParams(), PublicKeyMsg.class);
            PGPPublicKey publicKeyPbj = PgpUtil.loadPublicKey(publicKeyMsg.getPublicKey());
            String fingerprint = PgpUtil.getPublicKeyFingerprint(publicKeyPbj);
            if(!fingerprint.equals(publicKeyMsg.getTarget())){
                String errMsg = getType() + " receive publicKey fingerprint[" + publicKeyMsg.getTarget() + "] " +
                        "is not equals to publicKey";
                log.error(errMsg);
            }
            pgpManager.savePublicKey(publicKeyMsg.getTarget(), publicKeyPbj);
            routingTable.addNode(publicKeyMsg.getNode());
            routingTable.setNodeActive(publicKeyMsg.getTarget());
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    @Override
    protected Class<GetPublicKeyMsg> getParamsType() {
        return GetPublicKeyMsg.class;
    }

    @Override
    public String getType() {
        return Constant.MSG_TYPE_GET_PUBLIC_KEY;
    }

}
